#include "SketchModel.h"
#include "../display/Camera.h"
#include <iostream>
#include <fstream>
using namespace std;

CSketchModel::CSketchModel(CCamera* pCamera)
	: m_lineManager(&m_vertexManager), 
	m_vertexManager(&m_lineManager), 
	m_surfaceManager(&m_vertexManager, &m_lineManager, &m_curveManager),
	m_curveManager(&m_vertexManager, &m_lineManager)
{
	m_strokeVertices.clear();
	m_pCamera = pCamera;
	m_pActiveVertex = NULL;
}

CSketchModel::~CSketchModel()
{
	m_strokeVertices.clear();
	m_pCamera = NULL;
	m_pActiveVertex = NULL;
}

void CSketchModel::Init()
{
	m_pCamera->SetCameraParas();

	CVertex v;
	CScaffold s;
	v.m_v3D.SetValue(-SKE_INF_POINT, 0, 0);
	v.m_v2D = m_pCamera->Project(v.m_v3D);
	m_vertexManager.AddVertex(v);
	s.m_start = m_vertexManager.FindVertex(v);

	v.m_v3D.SetValue(SKE_INF_POINT, 0, 0);
	v.m_v2D = m_pCamera->Project(v.m_v3D);
	m_vertexManager.AddVertex(v);
	s.m_end = m_vertexManager.FindVertex(v);
	m_lineManager.AddScaffold(s);

	v.m_v3D.SetValue(0, -SKE_INF_POINT, 0);
	v.m_v2D = m_pCamera->Project(v.m_v3D);
	m_vertexManager.AddVertex(v);
	s.m_start = m_vertexManager.FindVertex(v);
	//m_lineManager.AddScaffold(s);

	v.m_v3D.SetValue(0, SKE_INF_POINT, 0);
	v.m_v2D = m_pCamera->Project(v.m_v3D);
	m_vertexManager.AddVertex(v);
	s.m_end = m_vertexManager.FindVertex(v);
	m_lineManager.AddScaffold(s);

	v.m_v3D.SetValue(0, 0, -SKE_INF_POINT);
	v.m_v2D = m_pCamera->Project(v.m_v3D);
	m_vertexManager.AddVertex(v);
	s.m_start = m_vertexManager.FindVertex(v);
	//m_lineManager.AddScaffold(s);

	v.m_v3D.SetValue(0, 0, SKE_INF_POINT);
	v.m_v2D = m_pCamera->Project(v.m_v3D);
	m_vertexManager.AddVertex(v);
	s.m_end = m_vertexManager.FindVertex(v);
	m_lineManager.AddScaffold(s);
}

void CSketchModel::SetCamera(CCamera* pCamera)
{
	m_pCamera = pCamera;
}

int CSketchModel::GetVertexCount()
{
	return m_vertexManager.GetVertexCount();
}

int CSketchModel::GetVertexCountWithoutInfPoint()
{
	return m_vertexManager.GetVertexCountWithoutInfPoint();
}

CVertex* CSketchModel::GetVertex(int index)
{
	return m_vertexManager.GetVertex(index);
}

int CSketchModel::GetLineSegmentCount()
{
	return m_lineManager.GetLineSegmentCount();
}

CLineSegment CSketchModel::GetLineSegment(int index)
{
	return m_lineManager.GetLineSegment(index);
}

int CSketchModel::GetScaffoldCount()
{
	return m_lineManager.GetScaffoldCount();
}

CScaffold CSketchModel::GetScaffold(int index)
{
	return m_lineManager.GetScaffold(index);
}

int CSketchModel::GetSurfaceCount()
{
	return m_surfaceManager.GetSurfaceCount();
}

CSurface CSketchModel::GetSurface(int index)
{
	return m_surfaceManager.GetSurface(index);
}

int CSketchModel::GetCurveCount()
{
	return m_curveManager.GetCurveCount();
}

CCurve CSketchModel::GetCurve(int index)
{
	return m_curveManager.GetCurve(index);
}

void CSketchModel::SetStrokes(CStrokeManager strokeManager)
{
	m_strokeVertices.clear();
	CVertex2DIter it;
	for (it = strokeManager.GetStrokeBeginIterator(); 
		it != strokeManager.GetStrokeEndIterator(); 
		it++)
	{
		m_strokeVertices.push_back(*it);
	}

}

CVertex CSketchModel::InferPoint(CVertex2D p2D)
{
	m_pCamera->SetCameraParas();
	int count = m_lineManager.GetScaffoldCount();
	float dist, minDist;
	CVertex v, minV;
	for (int i = 0; i < count; i++)
	{
		v = FindNearestVertex(p2D, m_lineManager.GetScaffold(i));
		dist = CalcDistance(p2D, v.m_v2D);
		if (i == 0 || dist < minDist)
		{
			minDist = dist;
			minV = v;
		}
	}
	CLineSegment l;
	CVertex3D start, end;
	count = m_lineManager.GetLineSegmentCount();
	for (int i = 0; i < count; i++)
	{
		l = m_lineManager.GetLineSegment(i);
		start = l.m_start->m_v3D;
		end = l.m_end->m_v3D;
		v = FindNearestVertex(p2D, l);
		dist = CalcDistance(p2D, v.m_v2D);
		if ((CVertex3D)(v.m_v3D - start) * (v.m_v3D - end) < -SKE_ERROR_THRESHOLD
			&& dist < minDist)
		{
			minDist = dist;
			minV = v;
		}
	}
	return minV;
}

CVertex CSketchModel::InferActivePoint(CVertex2D p2D)
{
	m_pCamera->SetCameraParas();
	int count = m_lineManager.GetScaffoldCount();
	float dist, minDist;
	CVertex v, minV;
	for (int i = 0; i < count; i++)
	{
		v = FindNearestVertex(p2D, m_lineManager.GetScaffold(i));
		dist = CalcDistance(p2D, v.m_v2D);
		if (i == 0 || dist < minDist)
		{
			minDist = dist;
			minV = v;
		}
	}
	
	if (CalcDistance(minV.m_v3D, m_pActiveVertex->m_v3D) > SKE_ERROR_THRESHOLD)
	{
		//	if m_pActiveVertex has got more than one scaffold on it,
		//	we have to add a new vertex, separate scaffolds and line segments
		//	then set the pointer to the new vertex

		//	else we only need to update the point
		CScaffoldIter it;
		int onScaffolds = 0;
		for (it = m_lineManager.m_lScaffold.begin(); it != m_lineManager.m_lScaffold.end(); it++)
		{
			if (IsInScaffold(m_pActiveVertex->m_v3D, *it))
			{
				onScaffolds++;
			}
			if (onScaffolds == 2)
				break;
		}
		if (onScaffolds == 2)
		{
			for (it = m_lineManager.m_lScaffold.begin(); it != m_lineManager.m_lScaffold.end(); it++)
			{
				if (it->m_start == m_pActiveVertex || it->m_end == m_pActiveVertex)
					break;
			}
			m_vertexManager.AddVertexFromHead(*m_pActiveVertex);
			if (it != m_lineManager.m_lScaffold.end())
			{
				m_vertexManager.AddVertexFromHead(*m_pActiveVertex);
				CVertex* pNewVertex = m_vertexManager.FindVertex(*m_pActiveVertex);
				for (; it != m_lineManager.m_lScaffold.end(); it++)
				{
					if (it->m_start == m_pActiveVertex)
						it->m_start = pNewVertex;
					else if (it->m_end == m_pActiveVertex)
						it->m_end = pNewVertex;
				}
			}
		}
		*m_pActiveVertex = minV;
	}
	return minV;
}

bool CSketchModel::InferScaffold()
{
	m_pCamera->SetCameraParas();

	int count = m_strokeVertices.size();
	CVertex *pStart, *pEnd;
	pStart = m_vertexManager.FindNearest2DVertex(m_strokeVertices[0]);
	pEnd = m_vertexManager.FindNearest2DVertex(m_strokeVertices[count - 1]);
	if (CalcDistance(pStart->m_v2D, m_strokeVertices[0]) < SKE_ERROR_POINT
		&& CalcDistance(pEnd->m_v2D, m_strokeVertices[count - 1]) < SKE_ERROR_POINT)
	{
		//	it is a line
		return false;
	}

	CVector2D v1;
	v1 = m_strokeVertices[0] - m_strokeVertices[count - 1];
	CVertex2D p1, p2;
	p1 = FindNearestStrokeVertex(pStart->m_v2D);
	p2 = FindNearestStrokeVertex(pEnd->m_v2D);
	if (CalcDistance(p1, pStart->m_v2D) < SKE_ERROR_POINT
		&& CalcDistance(p2, pEnd->m_v2D) < SKE_ERROR_POINT
		&& pStart != pEnd)
	{
		CScaffold s;
		s.m_start = pStart;
		s.m_end = pEnd;
		m_lineManager.AddScaffold(s);
		return true;
	}
	//	test for x/y/z-align scaffold
	//	if the angle between v1 and x/y/z unit is smaller
	//	than a threshold(30 degree in our implementation)
	//	we assume that they are the same
	CVertex v[3];
	CVector2D vAlign;
	for (int i = 0; i < 3; i++)
	{
		v[i] = *pStart;
		v[i].m_v3D.m_data[i] = SKE_INF_POINT;
		v[i].m_v2D = m_pCamera->Project(v[i].m_v3D);
		vAlign = v[i].m_v2D - pStart->m_v2D;
		if (CalcAngle(vAlign, v1) < M_PI / 6 || CalcAngle(vAlign, v1) > 5 * M_PI / 6)
		{
			//	a coordinate-aligned scaffold
			m_vertexManager.AddVertex(v[i]);
			CScaffold s;
			s.m_start = pStart;
			s.m_end = m_vertexManager.FindVertex(v[i]);
			m_lineManager.AddScaffold(s);
			return true;
		}
	}
	return false;
}

bool CSketchModel::InferLine()
{
	m_pCamera->SetCameraParas();
	CLineSegment l;
	CVertex *pStart, *pEnd;
	int count = m_strokeVertices.size();
	pStart = m_vertexManager.FindNearest2DVertex(m_strokeVertices[0]);
	pEnd = m_vertexManager.FindNearest2DVertex(m_strokeVertices[count - 1]);
	if (CalcDistance(pStart->m_v2D, m_strokeVertices[0]) < SKE_ERROR_POINT
		&& CalcDistance(pEnd->m_v2D, m_strokeVertices[count - 1]) < SKE_ERROR_POINT)
	{
		l.m_start = pStart;
		l.m_end = pEnd;
		m_lineManager.AddLineSegment(l);
		return true;
	}
	else
		return false;
}

bool CSketchModel::InferCurve()
{
	m_pCamera->SetCameraParas();
	CCurve curve;

	int count = m_strokeVertices.size();
	curve.m_start = m_vertexManager.FindNearest2DVertex(m_strokeVertices[0]);
	curve.m_end = m_vertexManager.FindNearest2DVertex(m_strokeVertices[count - 1]);
	
	CVector2D vStart, vEnd;
	//	find the direction for vStart and vEnd
	vStart = m_strokeVertices[(3 * SKE_ERROR_POINT > count) ? count - 1 : 3 * SKE_ERROR_POINT] 
		- m_strokeVertices[0];
	vEnd = m_strokeVertices[(count - 1 - 3 * SKE_ERROR_POINT < 0) ? 0 : count - 1 - 3 * SKE_ERROR_POINT] 
		- m_strokeVertices[count - 1];
	//	among all lines/scaffolds across m_start and m_end, find the nearest direction compared with vStart and vEnd
	CLineSegment startLine, endLine;
	float lineAngle, scaAngle;
	CLineSegment line = m_lineManager.GetNearestLineSegment(*curve.m_start, vStart);
	CScaffold scaffold = m_lineManager.GetNearestScaffold(*curve.m_start, vStart);
	if (line.m_start == NULL || line.m_end == NULL)
		lineAngle = M_PI;
	else
	{
		lineAngle = CalcAngle(line.m_end->m_v2D - line.m_start->m_v2D, vStart);
		if (lineAngle > M_PI / 2) lineAngle = M_PI - lineAngle;
	}
	scaAngle = CalcAngle(scaffold.m_end->m_v2D - scaffold.m_start->m_v2D, vStart);
	if (scaAngle > M_PI / 2) scaAngle = M_PI - scaAngle;
	startLine = (lineAngle < scaAngle) ? line : (CLineSegment)scaffold;
	
	line = m_lineManager.GetNearestLineSegment(*curve.m_end, vEnd);
	scaffold = m_lineManager.GetNearestScaffold(*curve.m_end, vEnd);
	if (line.m_start == NULL || line.m_end == NULL)
		lineAngle = M_PI;
	else
	{
		lineAngle = CalcAngle(line.m_end->m_v2D - line.m_start->m_v2D, vEnd);
		if (lineAngle > M_PI / 2) lineAngle = M_PI - lineAngle;
	}
	scaAngle = CalcAngle(scaffold.m_end->m_v2D - scaffold.m_start->m_v2D, vEnd);
	if (scaAngle > M_PI / 2) scaAngle = M_PI - scaAngle;
	endLine = (lineAngle < scaAngle) ? line : (CLineSegment)scaffold;

	//	if startLine and endLine intersects, add new knots
	SKE_LINEINTERSECT_SOLUTION type;
	CVertex v;
	v.m_v3D = ComputeIntersection(startLine.m_start->m_v3D, endLine.m_start->m_v3D, 
		startLine.m_end->m_v3D - startLine.m_start->m_v3D, 
		endLine.m_end->m_v3D - endLine.m_start->m_v3D,
		type);
	if (type == ONE_SOL)
	{
		v.m_v2D = ComputeIntersection(startLine.m_start->m_v2D, endLine.m_start->m_v2D,
			startLine.m_end->m_v2D - startLine.m_start->m_v2D,
			endLine.m_end->m_v2D - endLine.m_start->m_v2D, type);
		m_vertexManager.AddKnot(v);
		curve.m_mid = m_vertexManager.FindKnot(v);
		m_curveManager.AddCurve(curve);
		return true;
	}
	return false;
}

bool CSketchModel::InferSurface(int curveSegment)
{
	m_surfaceManager.InferSurface(curveSegment);
	return true;
}

void CSketchModel::UpdateVertices2D()
{
	m_pCamera->SetCameraParas();
	int count = m_vertexManager.GetVertexCount();
	CVertex* pV;
	for (int i = 0; i < count; i++)
	{
		pV = m_vertexManager.GetVertex(i);
		pV->m_v2D = m_pCamera->Project(pV->m_v3D);
	}
	count = m_vertexManager.GetKnotCount();
	for (int i = 0; i < count; i++)
	{
		pV = m_vertexManager.GetKnot(i);
		pV->m_v2D = m_pCamera->Project(pV->m_v3D);
	}
}

CVertex CSketchModel::FindNearestVertex(CVertex2D p2D, CLineSegment l)
{
	m_pCamera->SetCameraParas();
	CVertex start, end, v;
	start = *l.m_start;
	end = *l.m_end;
	CVector2D vl, vlpe;
	vl = end.m_v2D - start.m_v2D;
	vlpe.m_data[0] = vl.m_data[1];
	vlpe.m_data[1] = -vl.m_data[0];
	
	SKE_LINEINTERSECT_SOLUTION type;
	v.m_v2D = ComputeIntersection(p2D, start.m_v2D, vlpe, vl, type);
	CVertex3D cameraPos = m_pCamera->GetCameraPosition();
	CVertex3D p3D = m_pCamera->UnProject(v.m_v2D);
	v.m_v3D = ComputeIntersection(p3D, start.m_v3D, cameraPos - p3D, end.m_v3D - start.m_v3D, type);
	return v;
}

bool CSketchModel::LoadFromFile(const char* fileName)
{
	ifstream fin;
	fin.open(fileName);
	if (!fin.is_open())
		return false;
	m_strokeVertices.clear();
	m_pActiveVertex = NULL;

	//	m_vertexManager
		//vertex
		//knots
	int count;
	fin >> count;
	m_vertexManager.m_lVertex.clear();
	CVertex v;
	for (int i = 0; i < count; i++)
	{
		//fin >> v.m_v2D.m_data[0] >> v.m_v2D.m_data[1] 
		fin	>> v.m_v3D.m_data[0] >> v.m_v3D.m_data[1] >> v.m_v3D.m_data[2];
		m_vertexManager.m_lVertex.push_back(v);
	}
	fin >> count;
	m_vertexManager.m_lKnots.clear();
	for (int i = 0; i < count; i++)
	{
		//fin >> v.m_v2D.m_data[0] >> v.m_v2D.m_data[1] 
		fin	>> v.m_v3D.m_data[0] >> v.m_v3D.m_data[1] >> v.m_v3D.m_data[2];
		m_vertexManager.m_lKnots.push_back(v);
	}
	//	m_lineManager
		//lineSegment
		//scaffold
	fin >> count;
	m_lineManager.m_lSegment.clear();
	CVertex vStart, vEnd;
	CLineSegment line;
	for (int i = 0; i < count; i++)
	{
		fin >> vStart.m_v3D.m_data[0] >> vStart.m_v3D.m_data[1] >> vStart.m_v3D.m_data[2];
		line.m_start = m_vertexManager.FindVertex(vStart);
		fin >> vEnd.m_v3D.m_data[0] >> vEnd.m_v3D.m_data[1] >> vEnd.m_v3D.m_data[2];
		line.m_end = m_vertexManager.FindVertex(vEnd);
		m_lineManager.m_lSegment.push_back(line);
	}
	fin >> count;
	m_lineManager.m_lScaffold.clear();
	for (int i = 0; i < count; i++)
	{
		fin >> vStart.m_v3D.m_data[0] >> vStart.m_v3D.m_data[1] >> vStart.m_v3D.m_data[2];
		line.m_start = m_vertexManager.FindVertex(vStart);
		fin >> vEnd.m_v3D.m_data[0] >> vEnd.m_v3D.m_data[1] >> vEnd.m_v3D.m_data[2];
		line.m_end = m_vertexManager.FindVertex(vEnd);
		m_lineManager.m_lScaffold.push_back(line);
	}
	//	m_curveManager
		//lcurve
	fin >> count;
	m_curveManager.m_lCurve.clear();
	CCurve curve;
	CVertex vMid;
	for (int i = 0; i < count; i++)
	{
		fin >> vStart.m_v3D.m_data[0] >> vStart.m_v3D.m_data[1] >> vStart.m_v3D.m_data[2];
		curve.m_start = m_vertexManager.FindVertex(vStart);
		fin >> vMid.m_v3D.m_data[0] >> vMid.m_v3D.m_data[1] >> vMid.m_v3D.m_data[2];
		curve.m_mid = m_vertexManager.FindKnot(vMid);
		fin >> vEnd.m_v3D.m_data[0] >> vEnd.m_v3D.m_data[1] >> vEnd.m_v3D.m_data[2];
		curve.m_end = m_vertexManager.FindVertex(vEnd);
		m_curveManager.m_lCurve.push_back(curve);
	}
	//	m_surfaceManager
		//m_surface
	fin >> count;
	m_surfaceManager.m_surfaces.clear();
	CSurface surface;
	int vCount;
	CVertex3D vertex;
	for (int i = 0; i < count; i++)
	{
		surface.m_vertices.clear();
		fin >> vCount;
		for (int j = 0; j < vCount; j++)
		{
			fin >> vertex.m_data[0] >> vertex.m_data[1] >> vertex.m_data[2]; 
			surface.m_vertices.push_back(vertex);
		}
		fin >> surface.m_normal.m_data[0] >> surface.m_normal.m_data[1] >> surface.m_normal.m_data[2];
		m_surfaceManager.m_surfaces.push_back(surface);
	}
	UpdateVertices2D();
	fin.close();
	return true;
}

bool CSketchModel::SaveIntoFile(const char* fileName)
{
	ofstream fout;
	fout.open(fileName);
	if (!fout.is_open())
		return false;

	//	m_vertexManager
		//vertex
		//knots
	int count = m_vertexManager.GetVertexCount();
	fout << count << endl;
	CVertex* v;
	for (int i = 0; i < count; i++)
	{
		v = m_vertexManager.GetVertex(i);
		//fout << v->m_v2D.m_data[0] << ' ' << v->m_v2D.m_data[1] << ' '
		fout << v->m_v3D.m_data[0] << ' ' << v->m_v3D.m_data[1] << ' ' << v->m_v3D.m_data[2] << endl;
	}
	count = m_vertexManager.GetKnotCount();
	fout << count << endl;
	for (int i = 0; i < count; i++)
	{
		v = m_vertexManager.GetKnot(i);
		//fout << v->m_v2D.m_data[0] << ' ' << v->m_v2D.m_data[1] << ' '
		fout << v->m_v3D.m_data[0] << ' ' << v->m_v3D.m_data[1] << ' ' << v->m_v3D.m_data[2] << endl;
	}

	//	m_lineManager
		//	lSegment
		//	lScaffold
	count = m_lineManager.GetLineSegmentCount();
	fout << count << endl;
	CVertex *vStart, *vEnd;
	CLineSegment line;
	for (int i = 0; i < count; i++)
	{
		line = m_lineManager.GetLineSegment(i);
		vStart = line.m_start;
		vEnd = line.m_end;
		fout << vStart->m_v3D.m_data[0] << ' ' << vStart->m_v3D.m_data[1] << ' ' << vStart->m_v3D.m_data[2] << endl;
		fout << vEnd->m_v3D.m_data[0] << ' ' << vEnd->m_v3D.m_data[1] << ' ' << vEnd->m_v3D.m_data[2] << endl;
	}
	count = m_lineManager.GetScaffoldCount();
	fout << count << endl;
	CScaffold scaffold;
	for (int i = 0; i < count; i++)
	{
		scaffold = m_lineManager.GetScaffold(i);
		vStart = scaffold.m_start;
		vEnd = scaffold.m_end;
		fout << vStart->m_v3D.m_data[0] << ' ' << vStart->m_v3D.m_data[1] << ' ' << vStart->m_v3D.m_data[2] << endl;
		fout << vEnd->m_v3D.m_data[0] << ' ' << vEnd->m_v3D.m_data[1] << ' ' << vEnd->m_v3D.m_data[2] << endl;
	}
	//	m_curveManager
		//lcurve
	count = m_curveManager.GetCurveCount();
	fout << count << endl;
	CCurve curve;
	CVertex *vMid;
	for (int i = 0; i < count; i++)
	{
		curve = m_curveManager.GetCurve(i);
		vStart = curve.m_start;
		vMid = curve.m_mid;
		vEnd = curve.m_end;
		fout << vStart->m_v3D.m_data[0] << ' ' << vStart->m_v3D.m_data[1] << ' ' << vStart->m_v3D.m_data[2] << endl;
		fout << vMid->m_v3D.m_data[0] << ' ' << vMid->m_v3D.m_data[1] << ' ' << vMid->m_v3D.m_data[2] << endl;
		fout << vEnd->m_v3D.m_data[0] << ' ' << vEnd->m_v3D.m_data[1] << ' ' << vEnd->m_v3D.m_data[2] << endl;
	}

	//	m_surfaceManager
		//m_surface
	count = m_surfaceManager.GetSurfaceCount();
	fout << count << endl;
	CSurface surface;
	int vCount;
	for (int i = 0; i < count; i++)
	{
		surface = m_surfaceManager.GetSurface(i);
		vCount = surface.m_vertices.size();
		fout << vCount << endl;
		for (int j = 0; j < vCount; j++)
		{
			fout << surface.m_vertices[j].m_data[0] << ' '
				<< surface.m_vertices[j].m_data[1] << ' '
				<< surface.m_vertices[j].m_data[2] << endl;
		}
		fout << surface.m_normal.m_data[0] << ' ' 
			<< surface.m_normal.m_data[1] << ' ' 
			<< surface.m_normal.m_data[2] << endl;
	}

	fout.close();
	return true;
}

CVertex2D CSketchModel::FindNearestStrokeVertex(CVertex2D v)
{
	int count = m_strokeVertices.size();
	float minDist, dist;
	CVertex2D point, minPoint;
	minPoint = m_strokeVertices[0];
	minDist = CalcDistance(m_strokeVertices[0], v);
	for (int i = 1; i < count; i++)
	{
		dist = CalcDistance(m_strokeVertices[i], v);
		point = m_strokeVertices[i];
		if (dist < minDist)
		{
			minDist = dist;
			minPoint = point;
		}
	}
	return minPoint;
}

CVertex* CSketchModel::GetActiveVertex()
{
	return m_pActiveVertex;
}

void CSketchModel::SetActiveVertex(CVertex* vertex)
{
	m_pActiveVertex = vertex;
}